package com.taomus.mytools.simplec.utils;

import java.io.PrintWriter;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import org.objectweb.asm.util.TraceClassVisitor;


public class AsmUtils extends ClassLoader{
	
	private ClassWriter cw1 = null;
	private TraceClassVisitor cw = null;
	private transient static String className = null;
	
	private AsmUtils(){
		cw1 = new ClassWriter(ClassWriter.COMPUTE_MAXS);
		cw = new TraceClassVisitor(cw1,new PrintWriter(System.out));
	}
	public AsmUtils(String name){
		this();
		className = name;
	}
	public AsmUtils(String name,ICode code){
		this();
		className = name;
		this.createConstructor(code);
	}
	public void createConstructor(ICode code){
		cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC,className, null, "java/lang/Object", null);
		Method m = Method.getMethod("void <init>()");
		GeneratorAdapterUtils ga = new GeneratorAdapterUtils(className,Opcodes.ACC_PUBLIC, m, null, null, cw);
		ga.loadThis();
		ga.invokeConstructor(Type.getType(Object.class), m);
		if(null != code){
			code.code(ga);
		}
		ga.returnValue();
		ga.endMethod();
	}
	
	public void createVarArgsMethod(String m,ICode code){
		Method method = Method.getMethod(m);
		int defaultScope = Opcodes.ACC_PUBLIC+Opcodes.ACC_VARARGS;
		GeneratorAdapterUtils ga = new GeneratorAdapterUtils(className,defaultScope, method, null, null, cw);
		if(null != code){
			code.code(ga);
		}
		ga.returnValue();
		ga.endMethod();
	}
	public void createMethod(String m,ICode code){
		Method method = Method.getMethod(m);
		int defaultScope = Opcodes.ACC_PUBLIC;
		GeneratorAdapterUtils ga = new GeneratorAdapterUtils(className,defaultScope, method, null, null, cw);
		if(null != code){
			code.code(ga);
		}
		ga.returnValue();
		ga.endMethod();
	}
	private String createMethodName(String name){
		char[] field = name.toCharArray();
		if(field[0] >= 'a' && field[0] <= 'z'){
			field[0] = (char) (field[0] - 32);
		}
		return new String(field);
	}
	public void createField(String name,Class<?> tlass){
		int defaultScope = Opcodes.ACC_PRIVATE;
		cw.visitField(defaultScope, name,Type.getType(tlass).getDescriptor(),null,null).visitEnd();
		StringBuilder fieldName = new StringBuilder();
		fieldName.append("void set");
		fieldName.append(this.createMethodName(name));
		fieldName.append("(");
		fieldName.append(tlass.getSimpleName());
		fieldName.append(")");
		this.createMethod(fieldName.toString(),new ICode(){
			@Override
			public void code(GeneratorAdapterUtils adapter) {
				adapter.loadThis();
				adapter.loadArg(0);
				adapter.putField(name,tlass);
			}
		});
		fieldName = new StringBuilder();
		fieldName.append(tlass.getSimpleName());
		fieldName.append(" get");
		fieldName.append(this.createMethodName(name));
		fieldName.append("()");
		this.createMethod(fieldName.toString(),new ICode(){
			@Override
			public void code(GeneratorAdapterUtils adapter) {
				adapter.loadThis();
				adapter.getField(name,tlass);
			}
		});
	}
	
	public Class<?> loadClass2(String name){
		cw.visitEnd();
		byte[] bytes = cw1.toByteArray();
		System.out.println(bytes.length);
		Class<?> cl = this.defineClass(name,bytes,0,bytes.length);
		return cl;
	}
	
	public static String addValue(GeneratorAdapter ga,Object value){
		if(value instanceof Boolean){
			ga.push((Boolean)value);
			return "Boolean";
		}else if(value.equals(boolean.class)){
			ga.push((Boolean)value);
			return "boolean";
		}else if(value.equals(int.class)){
			ga.push((Integer)value);
			return "int";
		}else if(value instanceof Integer){
			ga.push((Integer)value);
			return "Integer";
		}else if(value.equals(double.class)){
			ga.push((Double)value);
			return "double";
		}else if(value instanceof Double){
			ga.push((Double)value);
			return "Double";
		}else if(value.equals(float.class)){
			ga.push((Float)value);
			return "float";
		}else if(value instanceof Float){
			ga.push((Float)value);
			return "Float";
		}else if(value.equals(long.class)){
			ga.push((Long)value);
			return "long";
		}else if(value instanceof Long){
			ga.push((Long)value);
			return "Long";
		}else if(value instanceof String){
			ga.push((String)value);
			return "String";
		}else if(value instanceof Handle){
			ga.push((Handle)value);
		}else if(value instanceof Type){
			ga.push((Type)value);
		}
		return "String";
	}
}